package simulation;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import util.Location;
import view.Canvas;


/**
 * Model holds the information for the simulation.
 *
 * @author Robert C. Duvall
 * edited by Schwartz & Roy
 */
public class Model {

    // bounds and input for game
    private Canvas myView;
    private UserInteraction myUser;
    private int myXBounds;
    private int myYBounds;

    // simulation state
    private Map<Integer, Mass> myLoadingMasses;
    private List<Mass> myMasses;
    private List<Spring> mySprings;
    private Environment myEnvironment;
    private boolean myPauseButton;

    /**
     * Create a game of the given size with the given display for its shapes.
     * @param canvas is the Canvas view model
     */
    public Model(Canvas canvas) {
        myView = canvas;
        myPauseButton = false;
        myLoadingMasses = new HashMap<Integer, Mass>();
        myMasses = new ArrayList<Mass>();
        mySprings = new ArrayList<Spring>();
        myEnvironment = new Environment();
        myXBounds = myView.getWidth();
        myYBounds = myView.getHeight();
        myUser = new UserInteraction(this, myView, myEnvironment);
    }

    /**
     * Draw all elements of the simulation.
     * @param pen is the graphics component
     */
    public void paint(Graphics2D pen) {
        pen.setColor(Color.BLACK);
        pen.fillRect(0, 0, myView.getWidth(), myView.getHeight());
        pen.setColor(Color.WHITE);
        pen.fillRect(0, 0, myXBounds, myYBounds);
        for (Spring s : mySprings) {
            s.paint(pen);
        }
        for (Mass m : myMasses) {
            m.paint(pen);
        }
    }

    /**
     * Update simulation for this moment, given the time since the last moment.
     * @param elapsedTime is the time since last update
     */
    public void update(double elapsedTime) {
        myUser.applyUserInteraction();
        Dimension bounds = new Dimension(myXBounds, myYBounds);
        if (!myPauseButton) {
            myEnvironment.update(elapsedTime, bounds, myMasses);
            Muscle.incrementTime(elapsedTime);
            for (Spring s : mySprings) {
                s.update(elapsedTime, bounds);
            }
            for (Mass m : myMasses) {
                m.update(elapsedTime, bounds);
            }
        }
    }

    /**
     * Clears out all current models.
     */
    public void clearAssemblies() {
        mySprings.clear();
        myMasses.clear();
    }

    public void modifyBounds(double changeX, double changeY){
        myXBounds += changeX;
        myYBounds += changeY;
    }

    public void changePauseState(){
        myPauseButton = !myPauseButton;
    }

    /**
     * Places the masses in the temporary hashmap into the main mass list.
     * clears the hap for next simulation load.
     */
    public void finalizeLoadingMasses() {
        myMasses.addAll(myLoadingMasses.values());
        myLoadingMasses.clear();
    }

    /**
     * Add given mass to this simulation.
     * @param id is the mass id, used for spring creation
     * @param mass is mass to add
     */
    public void add(int id, Mass mass) {
        myLoadingMasses.put(id, mass);
    }

    /**
     * Add given mass to this simulation.
     * @param mass is mass to add
     */
    public void add(Mass mass) {
        myMasses.add(mass);
    }

    /**
     * Add given spring to this simulation.
     * @param spring is spring to add
     */
    public void add(Spring spring) {
        mySprings.add(spring);
    }

    /**
     * gets the Mass with the specified id.
     * @param id is the desired Mass's id
     * @return the Mass
     */
    public Mass getMass(int id) {
        return myLoadingMasses.get(id);
    }

    /**
     * gets the list of Masses in this model.
     * @return List of Masses
     */
    public List<Mass> getMasses() {
        return Collections.unmodifiableList(myMasses);
    }

    /**
     * Removes mass.
     * @param m is the mass
     */
    public void remove(Mass m) {
        myMasses.remove(m);
    }

    /**
     * Removes spring.
     * @param s is the Spring
     */
    public void remove(Spring s) {
        mySprings.remove(s);
    }

    /**
     * @return the Environment for this Model
     */
    public Environment getEnvironment() {
        return myEnvironment;
    }
}
